#!/bin/sh /etc/rc.common
#
# Copyright (C) 2019 Chao Liu <expiron18@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#

USE_PROCD=1
START=99

confdir=/var/etc/kcptun
bindir=/usr/bin

mkjson_server_conf() {
	[ "$disabled" = 0 ] || return 1
	[ -n "$listen" ] || return 1
	[ -n "$target" ] || return 1
	[ -n "$target_port" ] || return 1
	json_add_string listen ":$listen"
	json_add_string target "$target:$target_port"
	json_add_boolean pprof "$pprof"
}

mkjson_client_conf() {
	[ "$disabled" = 0 ] || return 1
	[ -n "$local_port" ] || return 1
	[ -n "$server" ] || return 1
	[ -n "$server_port" ] || return 1
	json_add_string localaddr "$bind_address:$local_port"
	json_add_string remoteaddr "$server:$server_port"
	[ -z "$conn" ] || json_add_int conn "$conn"
	[ -z "$autoexpire" ] || json_add_int autoexpire "$autoexpire"
	[ -z "$scavengettl" ] || json_add_int scavengettl "$scavengettl"
}

kcptun() {
	local cfg="$1"
	local cfgtype="$2"
	local bin="$bindir/kcptun-$cfgtype"
	local confjson="$confdir/$cfgtype.$cfg.json"

	[ -x "$bin" ] || return
	eval "$("validate_${cfgtype}_section" "$cfg" validate_mklocal)"
	"validate_${cfgtype}_section" "$cfg" || return
	[ "$disabled" = 0 ] || return

	json_init
	mkjson_${cfgtype}_conf || return
	[ -z "$crypt" ] || json_add_string crypt "$crypt"
	[ -z "$key" ] || json_add_string key "$key"
	[ -z "$mode" ] || json_add_string mode "$mode"
	[ -z "$mtu" ] || json_add_int mtu "$mtu"
	[ -z "$sndwnd" ] || json_add_int sndwnd "$sndwnd"
	[ -z "$rcvwnd" ] || json_add_int rcvwnd "$rcvwnd"
	[ -z "$datashard" ] || json_add_int datashard "$datashard"
	[ -z "$parityshard" ] || json_add_int parityshard "$parityshard"
	[ -z "$dscp" ] || json_add_int dscp "$dscp"
	json_add_boolean nocomp "$nocomp"
	[ -z "$sockbuf" ] || json_add_int sockbuf "$sockbuf"
	[ -z "$smuxver" ] || json_add_int smuxver "$smuxver"
	[ -z "$smuxbuf" ] || json_add_int smuxbuf "$smuxbuf"
	[ -z "$streambuf" ] || json_add_int streambuf "$streambuf"
	[ -z "$keepalive" ] || json_add_int keepalive "$keepalive"
	[ -z "$snmplog" ] || json_add_string snmplog "$snmplog"
	[ -z "$snmpperiod" ] || json_add_int snmpperiod "$snmpperiod"
	json_add_boolean tcp "$tcp"
	json_add_boolean quiet "$quiet"
	json_dump -i > "$confjson"

	procd_open_instance "$cfgtype.$cfg"
	procd_set_param command "$bin" -c "$confjson"
	[ -z "$gogc" ] || procd_set_param env GOGC="$gogc"
	[ -z "$syslog" ] || procd_set_param stderr 1
	[ -z "$user" ] || procd_set_param user "$user"
	procd_set_param file "$confjson"
	procd_set_param respawn
	procd_close_instance
}

start_service() {
	local cfgtype

	mkdir -p "$confdir"
	config_load kcptun
	for cfgtype in server client; do
		config_foreach kcptun "$cfgtype" "$cfgtype"
	done
}

stop_service() {
	rm -rf "$confdir"
}

service_triggers() {
	procd_add_reload_interface_trigger wan
	procd_add_reload_trigger kcptun
	procd_open_validate
	validate_server_section
	validate_client_section
	procd_close_validate
}

validate_mklocal() {
	local tuple opts

	shift 2
	for tuple in "$@"; do
		opts="${tuple%%:*} $opts"
	done
	[ -z "$opts" ] || echo "local $opts"
}

validate() {
	uci_validate_section kcptun "$@"
}

validate_common_options() {
	local cfgtype="$1"; shift
	local cfg="$1"; shift
	local func="$1"; shift
	local crypt_methods='"aes", "aes-128", "aes-192", "salsa20", "blowfish", "twofish", "cast5", "3des", "tea", "xtea", "xor", "sm4", "none"'
	local mode_profiles='"fast3", "fast2", "fast", "normal", "manual"'

	"${func:-validate}" "$cfgtype" "$cfg" "$@" \
		'disabled:bool:0' \
		'key:string' \
		"crypt:or($crypt_methods)" \
		"mode:or($mode_profiles)" \
		'mtu:uinteger' \
		'sndwnd:uinteger' \
		'rcvwnd:uinteger' \
		'datashard:uinteger' \
		'parityshard:uinteger' \
		'dscp:uinteger' \
		'nocomp:bool' \
		'sockbuf:uinteger' \
		'smuxver:uinteger' \
		'smuxbuf:uinteger' \
		'streambuf:uinteger' \
		'keepalive:uinteger' \
		'snmplog:string' \
		'snmpperiod:uinteger' \
		'tcp:bool' \
		'quiet:bool' \
		'gogc:uinteger' \
		'syslog:bool:1' \
		'user:string:nobody'
}

validate_server_options() {
	validate_common_options server "$@" \
		'listen:or(port,portrange):29900-30000' \
		'target:host' \
		'target_port:port' \
		'pprof:bool'
}

validate_client_options() {
	validate_common_options client "$@" \
		'bind_address:ipaddr' \
		'local_port:port' \
		'server:host' \
		'server_port:or(port,portrange):29900-30000' \
		'conn:uinteger' \
		'autoexpire:uinteger' \
		'scavengettl:uinteger'
}

validate_server_section() {
	validate_server_options "$1" "$2"
}

validate_client_section() {
	validate_client_options "$1" "$2"
}
